﻿using System;
using ParserGenerator.GrammarConstruction;
using ParserGenerator.ParserConstruction;
using ParserGenerator.ScannerConstruction;
using ParserGenerator.SyntacticAnalysis;

namespace ParserGenerator.GrammarAnalysis
{
    [Serializable]
    internal sealed class GrammarAnalyzer
    {
        public GrammarDeclaration Grammar { get; }
        public ScannerAutomatonStartTable Starts { get; }
        public GrammarTerminalTable Tokens { get; }
        public LexicalPrecedenceTable LexicalPrecedences { get; }
        public GrammarGoalDescriptorTable Goals { get; }
        public GrammarNonterminalTable Nonterminals { get; }
        public GrammarProductionTable Productions { get; }
        public ExceptionDescriptorTable Exceptions { get; }
        public ScannerAutomaton ScannerAutomaton { get; }
        public ParserAutomaton ParserAutomaton { get; }

        internal GrammarAnalyzer(GrammarDeclaration grammar)
        {
            if (grammar == null)
                throw new ArgumentNullException(nameof(grammar));
            Grammar = grammar;
            Starts = new ScannerAutomatonStartTable(grammar);
            Tokens = new GrammarTerminalTable(grammar);
            Nonterminals = new GrammarNonterminalTable(grammar, Tokens);
            Goals = Nonterminals.Goals;
            Productions = Nonterminals.Productions;
            Exceptions = new ExceptionDescriptorTable(grammar);
            ScannerAutomaton = BuildScannerAutomaton();
            ParserAutomaton = BuildParserAutomaton();
        }

        private ScannerAutomaton BuildScannerAutomaton()
        {
            UserRuleTokenCollector collector = new UserRuleTokenCollector(Tokens);
            collector.ProcessLexicalDeclaration(Grammar.LexicalDeclaration);
            LexicalExpressionCompiler compiler = new LexicalExpressionCompiler(this);
            compiler.AddLexicalDeclaration(Tokens.CreateLiteralDeclaration());
            compiler.AddLexicalDeclaration(Grammar.LexicalDeclaration);
            compiler.Compile();
            ScannerAutomaton scannerAutomaton = new ScannerAutomaton();
            scannerAutomaton.Grammar = Grammar;
            scannerAutomaton.Starts = Starts;
            scannerAutomaton.Tokens = Tokens;
            scannerAutomaton.LexicalPrecedences = LexicalPrecedences;
            scannerAutomaton.Universal = compiler.Universal;
            scannerAutomaton.CharMap = compiler.CharMap;
            scannerAutomaton.Build();
            return scannerAutomaton;
        }

        private ParserAutomaton BuildParserAutomaton()
        {
            new ParserAutomatonItemTable(Productions);
            ParserAutomaton parserAutomaton = new ParserAutomaton();
            parserAutomaton.Grammar = Grammar;
            parserAutomaton.Goals = Goals;
            parserAutomaton.Nonterminals = Nonterminals;
            parserAutomaton.Tokens = Tokens;
            parserAutomaton.Productions = Productions;
            parserAutomaton.ScannerAutomaton = ScannerAutomaton;
            parserAutomaton.Build();
            return parserAutomaton;
        }
    }
}
